package com.songyang.yougnsql.core.table;

import com.songyang.yougnsql.common.ErrorMsg;
import com.songyang.yougnsql.common.ExceptionHandler;
import com.songyang.yougnsql.common.LogHelper;
import com.songyang.yougnsql.common.YoungSQLException;
import com.songyang.yougnsql.core.parser.statement.*;
import com.songyang.yougnsql.core.table.res.CalWhereRes;
import com.songyang.yougnsql.core.table.res.FieldCalRes;
import com.songyang.yougnsql.core.table.res.ParseValueRes;
import com.songyang.yougnsql.core.transaction.TransactionManager;
import com.songyang.yougnsql.core.utils.ByteUtils;
import com.songyang.yougnsql.core.utils.ParseStringRes;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.*;

/**
 * @ClassName Table
 * @Description 表
 * @date 2022/12/29 10:17
 * @Author yanceysong
 * @Version 1.0
 */
public class Table {
    private final List<Field> fields = new ArrayList<>();
    private final Logger logger = LogManager.getLogger(Table.class);
    public TableManager tbm;
    public String name;
    long uid;
    byte status;
    long nextUid;

    public Table(TableManager tableManager, long uid) {
        this.tbm = tableManager;
        this.uid = uid;
    }

    public Table(TableManager tbm, String tableName, long nextUid) {
        this.tbm = tbm;
        this.name = tableName;
        this.nextUid = nextUid;
    }

    /**
     * 创建一张表
     *
     * @param tableManager 表管理
     * @param nextUid      下一个表的uid
     * @param xid          事务的id
     * @param create       创建的抽象类
     * @return 表
     */
    public static Table createTable(TableManager tableManager, long nextUid, long xid, Create create) throws Exception {
        Table table = new Table(tableManager, create.tableName, nextUid);
        boolean hasIndex = false;
        for (int i = 0; i < create.fieldName.length; i++) {
            String fieldName = create.fieldName[i];
            String fieldType = create.fieldType[i];
            boolean indexed = false;
            for (int j = 0; j < create.index.length; j++) {
                if (fieldName.equals(create.index[j])) {
                    indexed = true;
                    hasIndex = true;
                    break;
                }
            }
            table.fields.add(Field.createField(table, xid, fieldName, fieldType, indexed));
        }
        if (!hasIndex) throw new YoungSQLException(ErrorMsg.TABLE_NO_INDEX_EXCEPTION);
        return table.persistSelf(xid);
    }

    /**
     * 加载一个Table
     *
     * @param tableManager 表管理
     * @param uid          表所在的uid
     * @return 一个表
     */
    public static Table loadTable(TableManager tableManager, long uid) {
        byte[] raw = null;
        try {
            raw = ((TableManagerImpl) tableManager).versionManager.read(TransactionManager.SUPER_XID, uid);
            Table table = new Table(tableManager, uid);
            return table.parseSelf(raw);
        } catch (Exception e) {
            ExceptionHandler.handler(e);
        }
        return null;
    }

    /**
     * 将表的数据写到文件
     *
     * @param xid 事务的id
     * @return 一张插入到文件的表
     */
    private Table persistSelf(long xid) throws Exception {
        byte[] nameRaw = ByteUtils.string2Byte(name);
        byte[] nextRaw = ByteUtils.long2Byte(nextUid);
        byte[] fieldRaw = new byte[0];
        for (Field field : fields) {
            fieldRaw = ByteUtils.concat(fieldRaw, ByteUtils.long2Byte(field.uid));
        }
        uid = ((TableManagerImpl) tbm).versionManager.insert(xid, ByteUtils.concat(nameRaw, nextRaw, fieldRaw));
        LogHelper.info(logger, "create:表的uid为:{}", uid);
        return this;
    }

    /**
     * 通过数据解析自己
     *
     * @param raw 数据
     * @return 表
     */
    private Table parseSelf(byte[] raw) {
        int pos = 0;
        ParseStringRes res = ByteUtils.parseString(raw);
        name = res.getStr();
        pos += res.getNext();
        nextUid = ByteUtils.parseLong(Arrays.copyOfRange(raw, pos, pos + 8));
        pos += 8;
        while (pos < raw.length) {
            long uid = ByteUtils.parseLong(Arrays.copyOfRange(raw, pos, pos + 8));
            pos += 8;
            fields.add(Field.loadField(this, uid));
        }
        return this;
    }

    /**
     * 执行where语句
     *
     * @param where where
     * @return 符合where条件的id集合
     */
    public List<Long> parseWhere(Where where) throws Exception {
        long l0 = 0, r0 = 0, l1 = 0, r1 = 0;
        boolean single = false;
        Field field = null;
        if (where == null) {
            for (Field f : fields) {
                if (f.isIndexed()) {
                    field = f;
                    break;
                }
            }
            //查全表
            r0 = Long.MAX_VALUE;
            single = true;
        } else {
            for (Field f : fields) {
                if (f.fieldName.equals(where.singleExp1.field)) {
                    if (!f.isIndexed()) throw new YoungSQLException(ErrorMsg.FIELD_NOT_INDEXED_EXCEPTION);
                    field = f;
                    break;
                }
            }
            if (field == null) throw new YoungSQLException(ErrorMsg.FIELD_NOT_FOUND_EXCEPTION);
            //得到要执行的左右子树的key
            CalWhereRes res = calWhere(field, where);
            l0 = res.l0;
            r0 = res.r0;
            l1 = res.l1;
            r1 = res.r1;
            single = res.single;
        }
        if (field == null) throw new YoungSQLException(ErrorMsg.FIELD_NOT_FOUND_EXCEPTION);
        List<Long> searchResult = field.search(l0, r0);
        //有or语句时候需要再查一次
        if (!single) {
            searchResult.addAll(field.search(l1, r1));
        }
        return searchResult;
    }

    /**
     * 执行where语句返回需要查询的key
     *
     * @param fd    字段
     * @param where where的抽象对象
     * @return 要查询的key左右
     * @throws Exception 异常
     */
    private CalWhereRes calWhere(Field fd, Where where) throws Exception {
        CalWhereRes res = new CalWhereRes();
        switch (where.logicOp) {
            case "":
                res.single = true;
                FieldCalRes r = fd.calExp(where.singleExp1);
                res.l0 = r.left;
                res.r0 = r.right;
                break;
            case "or":
                res.single = false;
                r = fd.calExp(where.singleExp1);
                res.l0 = r.left;
                res.r0 = r.right;
                r = fd.calExp(where.singleExp2);
                res.l1 = r.left;
                res.r1 = r.right;
                break;
            case "and":
                res.single = true;
                r = fd.calExp(where.singleExp1);
                res.l0 = r.left;
                res.r0 = r.right;
                r = fd.calExp(where.singleExp2);
                res.l1 = r.left;
                res.r1 = r.right;
                //查询的结果两次取交集
                if (res.l1 > res.l0) res.l0 = res.l1;
                if (res.r1 < res.r0) res.r0 = res.r1;
                break;
            default:
                throw new YoungSQLException(ErrorMsg.INVALID_LOG_OP_EXCEPTION);
        }
        return res;
    }

    /**
     * 在表中执行删除语句
     *
     * @param xid    事务的id
     * @param delete 删除的表达式
     * @return 删除以后受影响的行数
     */
    public int delete(long xid, Delete delete) throws Exception {
        List<Long> deleteUids = parseWhere(delete.where);
        int count = 0;
        for (Long uid : deleteUids) {
            if (((TableManagerImpl) tbm).versionManager.delete(xid, uid)) {
                count++;
            }
        }
        return count;
    }

    /**
     * 执行插入语句
     *
     * @param xid    事务的id
     * @param insert 插入语句抽象
     */
    public void insert(long xid, Insert insert) throws Exception {
        Map<String, Object> entry = string2Entry(insert.values);
        //所有字段字节拼装
        byte[] raw = entry2Raw(entry);
        long uid = ((TableManagerImpl) tbm).versionManager.insert(xid, raw);
        LogHelper.info(logger, "insert:事务id为{}的数据uid:{}", xid, uid);
        for (Field field : fields) {
            if (field.isIndexed()) {
                LogHelper.info(logger,"insert:插入的索引为{}",field.fieldName);
                //值,数据的uid
                field.insert(entry.get(field.fieldName), uid);
            }
        }
    }

    /**
     * 实体转成字节数组
     *
     * @param entry 映射好的实体
     * @return 字节数组
     */
    private byte[] entry2Raw(Map<String, Object> entry) {
        byte[] raw = new byte[0];
        for (Field field : fields) {
            raw = ByteUtils.concat(raw, field.value2Raw(entry.get(field.fieldName)));
        }
        return raw;
    }

    /**
     * 插入或者更新的值与字段进行映射后返回
     *
     * @param values 要插入的值
     * @return 映射后的实体
     * @throws YoungSQLException 异常
     */
    private Map<String, Object> string2Entry(String[] values) throws YoungSQLException {
        if (values.length != fields.size()) {
            //这里意味着不支持默认值，要插入的值必须和表的字段一一对应
            throw new YoungSQLException(ErrorMsg.INVALID_VALUES_EXCEPTION);
        }
        HashMap<String, Object> entryMap = new HashMap<>();
        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            entryMap.put(field.fieldName, field.string2Value(values[i]));
        }
        return entryMap;
    }

    /**
     * 将实体按照 特定的格式进行打印
     *
     * @param entry 实体
     * @return 打印后的字符串拼接
     */
    private String printEntry(Map<String, Object> entry) {
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            sb.append(field.printValue(entry.get(field.fieldName)));
            if (i == fields.size() - 1) {
                sb.append("]");
            } else {
                sb.append(", ");
            }
        }
        return sb.toString();
    }

    /**
     * 执行查询语句
     *
     * @param xid    事务的id
     * @param select 查询语句抽象
     * @return 查询的记过
     */
    public String read(long xid, Select select) throws Exception {
        List<Long> selectUids = parseWhere(select.where);
        StringBuilder sb = new StringBuilder();
        LogHelper.info(logger, "事务id:{}查询符合条件的uid为{}", xid, selectUids.toString());
        for (Long uid : selectUids) {
            byte[] readResult = ((TableManagerImpl) tbm).versionManager.read(xid, uid);
            if (readResult == null) continue;
            //插入的时候按照实体插入，读取的时候也按照相同的格式读取
            sb.append(printEntry(parseEntry(readResult))).append("\n");
        }
        return sb.toString();
    }

    /**
     * 因为才插入的时候是按照entry插入的，所以读取的时候也按照这个进行解析
     *
     * @param raw 读取的数据
     * @return 解析的entry
     */
    private Map<String, Object> parseEntry(byte[] raw) {
        int pos = 0;
        Map<String, Object> entry = new HashMap<>();
        for (Field field : fields) {
            //因为插入是按照字段的顺序插入的，读取的时候也按照字段的顺序进行读取。
            ParseValueRes r = field.parserValue(Arrays.copyOfRange(raw, pos, raw.length));
            entry.put(field.fieldName, r.v);
            pos += r.shift;
        }
        return entry;
    }

    /**
     * 执行更新语句
     *
     * @param xid    事务的id
     * @param update 更新语句的抽象
     * @return 受影响的行数
     * @throws Exception 异常
     */
    public int update(long xid, Update update) throws Exception {
        //找到符合规范的uids
        List<Long> updateUids = parseWhere(update.where);
        Field field = null;
        //确定更新字段
        for (Field f : fields) {
            if (f.fieldName.equals(update.fieldName)) {
                field = f;
                break;
            }
        }
        if (field == null) throw new YoungSQLException(ErrorMsg.FIELD_NOT_FOUND_EXCEPTION);
        int count = 0;
        for (Long uid : updateUids) {
            //读取所有uid下面的数据
            byte[] raw = ((TableManagerImpl) tbm).versionManager.read(xid, uid);
            if (raw == null) continue;
            // delete 操作
            ((TableManagerImpl) tbm).versionManager.delete(xid, uid);
            //开始insert
            Map<String, Object> entry = parseEntry(raw);
            //替换新的数据
            entry.put(field.fieldName, field.string2Value(update.value));
            raw = entry2Raw(entry);
            //该条记录新的uid地址
            long uuid = ((TableManagerImpl) tbm).versionManager.insert(xid, raw);
            count++;
            for (Field f : fields) {
                //开始更新表中所有的索引数据
                if (f.isIndexed()) {
                    f.insert(entry.get(f.fieldName), uuid);
                }
            }
        }
        return count;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("{");
        sb.append(name).append(": ");
        for (Field field : fields) {
            sb.append(field.toString());
            if (field == fields.get(fields.size() - 1)) {
                sb.append("}");
            } else {
                sb.append(", ");
            }
        }
        return sb.toString();
    }
}
